﻿using DevelopTool.Bll.LocalDbBlls;
using DevelopTool.Bll.Properties;
using DevelopTool.Common;
using DevelopTool.Model.ChatModels;
using DevelopTool.Model.Enums;
using LiteDB;
using Newtonsoft.Json;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;

namespace DevelopTool.Bll.ChatBlls
{
    public class ChatBll
    {
        /// <summary>
        /// 用户信息改变委托
        /// </summary>
        /// <param name="chatUser"></param>
        public delegate void ChatUserChangedEventHandler(ChatUser chatUser);


        /// <summary>
        /// 用户信息改变事件
        /// </summary>
        public static event ChatUserChangedEventHandler ChatUserChangedEvent;



        /// <summary>
        /// 接收到新消息委托
        /// </summary>
        /// <param name="sendChatUser"></param>
        /// <param name="chatRecord"></param>
        public delegate void NewMessageEventHandler(ChatRecord chatRecord);

        /// <summary>
        /// 接收到新消息事件
        /// </summary>
        public static event NewMessageEventHandler ChatNewMessageEvent;



        /// <summary>
        /// 删除聊天消息事件
        /// </summary>
        public static event NewMessageEventHandler ChatDeleteMessageEvent;


        public ChatBll()
        {
            try
            {
                using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
                {
                    var col_ChatUser = db.GetCollection<ChatUser>();
                    col_ChatUser.EnsureIndex(x => x.UserId);
                    col_ChatUser.EnsureIndex(x => x.NikeName);
                    col_ChatUser.EnsureIndex(x => x.RemarkName);

                    var col_ChatRecord = db.GetCollection<ChatRecord>();
                    col_ChatRecord.EnsureIndex(x => x.CreateTime);
                    col_ChatRecord.EnsureIndex(x => x.SendUser.UserId);
                    col_ChatRecord.EnsureIndex(x => x.ReceiverUser.UserId);
                    col_ChatRecord.EnsureIndex(x => x.ReceiverUser.UserId);
                    col_ChatRecord.EnsureIndex(x => x.ChatFileMetaData.FileId);
                }
            }
            catch (Exception ex)
            {
            }
        }


        /// <summary>
        /// 启动聊天相关服务
        /// </summary>
        public void StartChatService()
        {
            if (!Setting.Chat.Enable) return;//已启用聊天功能                                   

            if (string.IsNullOrEmpty(Setting.Chat.UserId))
                Setting.Chat.UserId = Guid.NewGuid().ToString("N");//生成用户id

            AllUserOffLine();//所有用户离线

            //检测端口占用
            if (PortHelper.IsTcpPortInUse(Setting.Chat.Port) || PortHelper.IsUdpPortInUse(Setting.Chat.Port))
            {
                throw new Exception($"局域网聊天服务启动失败，通讯端口[{Setting.Chat.Port}]已被占用，请更换其它端口重试！");
            };            
            
            ChatNetworkBll.TimedSendDiscoveryBroadcastToAllNetworkSegments();//启动定时发送网络发现广播消息

            ChatNetworkBll.StartListen(Setting.Chat.Port);//启动UDP/http消息监听
        }


        /// <summary>
        /// 清空所有用户（测试用）
        /// </summary>
        private void ClearAll()
        {

            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatUser>();
                col.Delete(it => it.Id > 0);
            }
        }


        /// <summary>
        /// 所有用户离线
        /// </summary>
        public void AllUserOffLine()
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatUser>();
                var users = col.FindAll().ToList();

                for (int i = 0; i < users.Count; i++)
                {
                    users[i].IsLine = false;
                }
                col.Update(users);
            }
        }


        /// <summary>
        /// 获取当前用户所有信息
        /// </summary>
        /// <returns></returns>
        public ChatUser GetCurrentUserAllInfo()
        {
            return new ChatUser
            {
                UserId = Setting.Chat.UserId,//用户id
                Port = Setting.Chat.Port,//端口号
                NikeName = Setting.Chat.NikeName,//昵称
                ComputerName = System.Environment.MachineName,//获取计算机名称
                SystemUserName = System.Environment.UserName,//获取当前系统用户名称 
                SystemOsName = WindowsHelper.GetSystemOsName(),//获取系统版本 
                AppName = GlobalData.AppName_zh + " V" + System.Windows.Forms.Application.ProductVersion.ToString()//应用名称
            };
        }


        /// <summary>
        /// 获取当前用户重要信息
        /// </summary>
        /// <returns></returns>
        public dynamic GetCurrentUserImportantInfo()
        {
            return new
            {
                UserId = Setting.Chat.UserId,//用户id
                NikeName = Setting.Chat.NikeName,
                ComputerName = System.Environment.MachineName,//获取计算机名称
                SystemUserName = System.Environment.UserName,//获取当前系统用户名称 
                SystemOsName = WindowsHelper.GetSystemOsName(),//获取系统版本 
                AppName = GlobalData.AppName_zh + " V" + System.Windows.Forms.Application.ProductVersion.ToString()//应用名称
            };
        }


        /// <summary>
        /// 获取所有有备注的用户
        /// </summary>
        /// <returns></returns>
        public List<ChatUser> GetAllUserWithReamrkList()
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                return db.GetCollection<ChatUser>().Find(it => !string.IsNullOrEmpty(it.RemarkName)).ToList();

            }
        }



        /// <summary>
        /// 根据id获取用户
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public ChatUser GetUserByUserId(string userId)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatUser>();

                var user = col.FindOne(it => it.UserId == userId);

                return user;
            }
        }


        /// <summary>
        /// 设置用户备注名
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="remarkName"></param>
        public ChatUser SetChatUserRemarkName(string userId, string remarkName)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatUser>();

                var user = col.FindOne(it => it.UserId == userId);

                user.RemarkName = remarkName;

                col.Update(user);

                return user;
            }
        }


        /// <summary>
        /// 接收并保存用户信息
        /// </summary>
        /// <param name="ipEndPoint"></param>
        /// <param name="userInfo"></param>
        public void ReceiveUserInfo(string ip, int port, string userInfo)
        {
            try
            {
                //解析用户信息
                ChatUser chatUser = userInfo.DecryptAndDecompress().DeserializeObject<ChatUser>();

                chatUser.IsLine = true;
                chatUser.IP = ip;
                chatUser.Port = port;

                chatUser.Id = 0;

                using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
                {
                    var col = db.GetCollection<ChatUser>();

                    ChatUser user = col.FindOne(it => it.UserId == chatUser.UserId);
                    if (user != null)
                    {
                        chatUser.Id = user.Id;
                        chatUser.RemarkName = user.RemarkName;
                        chatUser.CreateTime = user.CreateTime;
                        chatUser.Remark = user.Remark;
                    }

                    chatUser.UpdateTime = DateTime.Now;

                    col.Upsert(chatUser);

                    chatUser = col.FindOne(it => it.UserId == chatUser.UserId);
                }

                ChatUserChangedEvent?.Invoke(chatUser);
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrLog(ex, false);
            }
        }



        /// <summary>
        /// 设置用户在线状态
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="isLine"></param>
        public void SetUserLineState(string userId, bool isLine)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatUser>();

                ChatUser user = col.FindOne(it => it.UserId == userId);
                if (user != null)
                {
                    user.IsLine = isLine;
                    user.UpdateTime = DateTime.Now;
                    col.Update(user);
                    ChatUserChangedEvent?.Invoke(user);
                }
            }
        }


        /// <summary>
        /// 获取有会话的用户集合
        /// </summary>
        /// <returns></returns>
        public List<ChatUser> GetChatSessionUsers()
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();

                var all = col.FindAll();

                var sendUserIds = all.GroupBy(it => it.SendUser.UserId).Select(it => it.Key).ToList();

                var receiverUserIds = all.GroupBy(it => it.ReceiverUser.UserId).Select(it => it.Key).ToList();

                sendUserIds.AddRange(receiverUserIds);

                var users = sendUserIds.Distinct().ToList();

                var ucol = db.GetCollection<ChatUser>();

                return ucol.Find(it => users.Contains(it.UserId)).ToList();
            }
        }


        /// <summary>
        /// 获取最后一个聊天记录
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public ChatRecord GetLastChatRecord(string userId)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();

                return col.Find(it => it.ReceiverUser.UserId == userId || it.SendUser.UserId == userId).OrderByDescending(it => it.CreateTime).FirstOrDefault();

            }
        }


        /// <summary>
        /// 删除用户聊天会话
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public int DeleteUserRecord(string userId)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();

                int result = col.Delete(it => it.ReceiverUser.UserId == userId || it.SendUser.UserId == userId);//从数据库删除聊天记录

                if (result > 0)
                {
                    //删除聊天文件
                    string targetDirectory = Path.Combine(Setting.Chat.BinarySavePath, userId);

                    try
                    {
                        Directory.Delete(targetDirectory, true);
                    }
                    catch { }

                }

                return result;

            }
        }

        /// <summary>
        /// 删除单个聊天消息
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public int DeleteSingleRecord(ChatRecord chatRecord)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();

                int result = col.Delete(it => it.Id == chatRecord.Id);//从数据库删除聊天记录

                //如果是文件或图片
                if (result > 0 && (chatRecord.ChatMessageType == ChatMessageType.File || chatRecord.ChatMessageType == ChatMessageType.Image))
                {
                    if (chatRecord.ChatFileMetaData.FileLocalPath.Contains(Setting.Chat.BinarySavePath))
                    {
                        Directory.Delete(chatRecord.ChatFileMetaData.FileLocalPath, true);//删除聊天文件
                    }
                }

                ChatDeleteMessageEvent?.Invoke(chatRecord);//触发事件

                return result;
            }
        }


        /// <summary>
        /// 获取聊天记录集合
        /// </summary>
        /// <returns></returns>
        public List<ChatRecord> GetSessionUserRecordList(string sessionUserId)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var models = db.GetCollection<ChatRecord>().Find(it => it.ReceiverUser.UserId == sessionUserId || it.SendUser.UserId == sessionUserId);

                return models.OrderBy(it => it.CreateTime).ToList();
            }
        }


        /// <summary>
        /// 插入聊天记录
        /// </summary>
        /// <param name="chatRecord"></param>
        public void SaveChatRecord(ChatRecord chatRecord)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();
                col.Upsert(chatRecord);
            }
        }




        /// <summary>
        /// 接收文本消息
        /// </summary>
        public ChatRecord ReceiveTextMessage(string message)
        {
            ChatRecord chatRecord = message.DecryptAndDecompress().DeserializeObject<ChatRecord>();//解析消息

            //发送消息的用户
            chatRecord.SendUser = this.GetUserByUserId(chatRecord.SendUser.UserId);

            //接收消息的用户（当前用户）
            var receiverUser = GetCurrentUserAllInfo();
            receiverUser.IP = chatRecord.ReceiverUser.IP;
            chatRecord.ReceiverUser = receiverUser;

            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();
                col.Insert(chatRecord);
            }

            ChatNewMessageEvent?.Invoke(chatRecord);

            return chatRecord;
        }


        /// <summary>
        /// 接收文件元数据
        /// </summary>
        /// <param name="message"></param>
        /// <param name="remoteIp"></param>
        /// <param name="remotePort"></param>
        /// <param name="localIp"></param>
        /// <param name="localPort"></param>
        public ChatRecord ReceiveFileMetaData(string message)
        {

            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                ChatRecord chatRecord = message.DecryptAndDecompress().DeserializeObject<ChatRecord>();//解析消息

                //发送消息的用户
                chatRecord.SendUser = this.GetUserByUserId(chatRecord.SendUser.UserId);

                //接收消息的用户（当前用户）
                var receiverUser = GetCurrentUserAllInfo();
                receiverUser.IP = chatRecord.ReceiverUser.IP;
                chatRecord.ReceiverUser = receiverUser;

                var col_ChatRecord = db.GetCollection<ChatRecord>();

                col_ChatRecord.Upsert(chatRecord);

                //ChatNewMessageEvent?.Invoke(chatRecord);

                // 分片文件临时目录
                string chunkDir = Path.Combine(GlobalData.AppTempDirectory, "FileChunkData", chatRecord.ChatFileMetaData.FileId);
                if (Directory.Exists(chunkDir))
                {
                    //获取已经传输的分片文件索引集合
                    chatRecord.ChatFileMetaData.ChunkIndexList = Directory.GetFileSystemEntries(chunkDir, "*.chunk").Select(it => Path.GetFileNameWithoutExtension(it).ToInt()).ToList();
                }
                else
                {
                    chatRecord.ChatFileMetaData.ChunkIndexList = new List<int>();
                }

                return chatRecord;
            }

        }



        /// <summary>
        /// 接收文件分片数据
        /// </summary>
        /// <param name="chatMessage"></param>
        /// <param name="chatFileChunkData"></param>
        public void ReceiveFileChunkData(ChatFileChunkData chatFileChunkData, string fileLocalPath)
        {
            using (var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<ChatRecord>();
                ChatRecord chatRecord = col.FindOne(it => it.ChatFileMetaData.FileId == chatFileChunkData.FileId);

                //fileLocalPath不为空时表示已接收完成
                if (string.IsNullOrEmpty(fileLocalPath))
                {
                    //设置接收进度
                    chatRecord.ChatFileMetaData.Progres = ((chatFileChunkData.ChunkIndex + 1) * 100.0 / chatFileChunkData.TotalChunks).ToInt();
                }
                else
                {
                    chatRecord.ChatFileMetaData.FileName = Path.GetFileName(fileLocalPath);
                    chatRecord.ChatFileMetaData.FileLocalPath = fileLocalPath;
                    chatRecord.ChatFileMetaData.Progres = 100;
                    col.Update(chatRecord);
                    ChatNewMessageEvent?.Invoke(chatRecord);
                }


            }

        }





        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="chatRecord"></param>
        /// <param name="messageType"></param>
        /// <param name="compress"></param>
        /// <returns></returns>
        public ChatRecord SendMessage(ChatRecord chatRecord, string messageType, bool compress = true)
        {
            try
            {
                string objectToSend = chatRecord.SerializeObject();
                if (compress) objectToSend = objectToSend.EncryptAndCompress();

                //发送请求
                HttpHelper httpHelper = new HttpHelper();
                HttpItem item = new HttpItem()
                {
                    URL = $"http://{chatRecord.ReceiverUser.IP}:{chatRecord.ReceiverUser.Port}/{messageType}",
                    Method = "POST",
                    Postdata = objectToSend
                };
                HttpResult result = httpHelper.GetData(item);

                if (result.StatusCode == HttpStatusCode.OK)
                {
                    string resSrt = result.Data.DecryptAndDecompress();
                    ChatMessageReceiveResult receiveResult = JsonConvert.DeserializeObject<ChatMessageReceiveResult>(resSrt);

                    chatRecord.SendUser.IP = receiveResult.SendIp;
                    chatRecord.SendUser.Port = Setting.Chat.Port;

                    chatRecord.SendState = receiveResult.ReceiveState;
                    chatRecord.SendResult = receiveResult.Message;
                }
                else
                {
                    chatRecord.SendState = ChatMessageSendState.Offline;
                    chatRecord.SendResult = result.Data;
                }
            }
            catch (Exception ex)
            {
                chatRecord.SendState = ChatMessageSendState.SendError;
                chatRecord.SendResult = "错误：" + ex.Message;
            }

            return chatRecord;
        }


        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="chatRecord"></param>
        /// <param name="sendFileCTS"></param>
        /// <param name="sendProgress"></param>
        /// <returns></returns>
        public ChatRecord SendFile(ChatRecord chatRecord, CancellationTokenSource sendFileCTS, Action<double> sendProgress)
        {
            try
            {
                int chunkSize = 1048576;//默认分块大小

                if (chatRecord.ChatFileMetaData.Length <= chunkSize) chunkSize = chatRecord.ChatFileMetaData.Length.ToInt();

                //计算文件分片数量
                chatRecord.ChatFileMetaData.ChunkCount = (int)Math.Ceiling((double)chatRecord.ChatFileMetaData.Length / chunkSize);

                chatRecord = SendMessage(chatRecord, "FileMetaData");

                if (chatRecord.SendState != ChatMessageSendState.Success) return chatRecord;


                // 打开文件流读取文件
                using (FileStream fileStream = new FileStream(chatRecord.ChatFileMetaData.FileLocalPath, System.IO.FileMode.Open, FileAccess.Read, FileShare.Read, chunkSize, false))
                {
                    byte[] buffer = new byte[chunkSize];
                    int bytesRead;

                    for (int i = 0; i < chatRecord.ChatFileMetaData.ChunkCount; i++)
                    {
                        if (sendFileCTS.IsCancellationRequested)
                        {
                            chatRecord.SendState = ChatMessageSendState.Cancel;
                            chatRecord.SendResult = "手动取消发送";
                            break;
                        }

                        // 读取文件分片
                        bytesRead = fileStream.Read(buffer, 0, chunkSize);

                        if (chatRecord.ChatFileMetaData.ChunkIndexList != null && chatRecord.ChatFileMetaData.ChunkIndexList.Contains(i)) continue;//跳过已经传输的文件分片数据

                        byte[] dataToSend = new byte[bytesRead];
                        Array.Copy(buffer, dataToSend, bytesRead); // 复制当前读取的数据到新的数组

                        // 创建文件分片数据对象
                        ChatFileChunkData chatFileChunkData = new ChatFileChunkData()
                        {
                            UserId = Setting.Chat.UserId,
                            FileId = chatRecord.ChatFileMetaData.FileId,
                            FileName = chatRecord.ChatFileMetaData.FileName,
                            ChunkIndex = i,
                            TotalChunks = chatRecord.ChatFileMetaData.ChunkCount,
                            Data = dataToSend // 发送复制后的数据
                        };


                        //发送请求
                        HttpHelper httpHelper = new HttpHelper();
                        HttpItem item = new HttpItem()
                        {
                            URL = $"http://{chatRecord.ReceiverUser.IP}:{chatRecord.ReceiverUser.Port}/FileChunkData",
                            Method = "POST",
                            Postdata = chatFileChunkData.SerializeObject().EncryptAndCompress()
                        };
                        HttpResult result = httpHelper.GetData(item);

                        if (result.StatusCode == HttpStatusCode.OK)
                        {
                            string resSrt = result.Data.DecryptAndDecompress();
                            ChatMessageReceiveResult receiveResult = JsonConvert.DeserializeObject<ChatMessageReceiveResult>(resSrt);

                            if (receiveResult.ReceiveState == ChatMessageSendState.Refuse)
                            {
                                chatRecord.SendResult = "用户拒绝接收";
                                break;
                            }

                            if (receiveResult.ReceiveState == ChatMessageSendState.Cancel)
                            {
                                chatRecord.SendResult = "用户取消接收";
                                break;
                            }

                            if (receiveResult.ReceiveState != ChatMessageSendState.Success)
                            {
                                chatRecord.SendResult = receiveResult.Message;
                                continue;
                            }
                        }
                        else
                        {
                            chatRecord.SendState = ChatMessageSendState.Offline;
                            chatRecord.SendResult = result.Data;
                            continue;
                        }

                        // 更新发送进度
                        sendProgress?.Invoke((i + 1) * 100.0 / chatRecord.ChatFileMetaData.ChunkCount); // 发送进度百分比
                    }
                }

            }
            catch (Exception ex)
            {
                chatRecord.SendState = ChatMessageSendState.SendError;
                chatRecord.SendResult = "错误：" + ex.Message;
            }

            return chatRecord;
        }


        /// <summary>
        /// 音频提醒
        /// </summary>
        public static void NotifyAudio()
        {
            if (Setting.Chat.NotifyAudio)
                WavPlayerHelper.Play(Resource.chatnotify, WavPlayerHelper.AudioPlayMode.Background);//后台播放声音
        }

        /// <summary>
        /// 右下角弹窗提醒
        /// </summary>
        /// <param name="message"></param>
        public static void NotifyPop(string message)
        {
            if (Setting.Chat.NotifyPop && !string.IsNullOrEmpty(message))
                UINotifierHelper.ShowNotifier(message, UINotifierType.INFO, UIStyles.CurrentResources.InfoTitle, false, 2000);
        }

    }
}
